package excel

import (
	"database/sql"
	"errors"
	"fmt"
	"reflect"
	"strconv"
	"strings"

	"github.com/360EntSecGroup-Skylar/excelize"
)

type Scanner interface {
	Excel(src string) error
}

func New() *excelize.File {
	f := excelize.NewFile()
	return f
}
func Cell(col int, row int) string {
	c := col
	bt := make([]byte, 0)
	for c > 0 {
		m := (c - 1) % 26
		bt = append([]byte{uint8(m) + 65}, bt...)
		c = (c - m) / 26
	}
	return fmt.Sprintf("%s%d", bt, row)
}
func ExportHeader(f *excelize.File, x interface{}) error {
	index := f.GetActiveSheetIndex()
	sheet := f.GetSheetName(index)
	kind := reflect.TypeOf(x)
	if kind == nil {
		return fmt.Errorf("导出对象不可空")
	}
	if kind.Kind() == reflect.Ptr {
		kind = kind.Elem()
	}
	if kind.Kind() != reflect.Struct {
		return fmt.Errorf("导出对象必需是struct类型")
	}
	for i := 0; i < kind.NumField(); i++ {
		field := kind.Field(i)
		name := field.Name
		if comment, ok := field.Tag.Lookup("comment"); ok {
			name = comment
		}
		f.SetCellValue(sheet, Cell(i+1, 1), name)
	}
	return nil
}

var errNilPtr = errors.New("destination pointer is nil")

func strconvErr(err error) error {
	if ne, ok := err.(*strconv.NumError); ok {
		return ne.Err
	}
	return err
}

func cloneBytes(b []byte) []byte {
	if b == nil {
		return nil
	}
	c := make([]byte, len(b))
	copy(c, b)
	return c
}

func asString(src interface{}) string {
	switch v := src.(type) {
	case string:
		return v
	case []byte:
		return string(v)
	}
	rv := reflect.ValueOf(src)
	switch rv.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(rv.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.FormatUint(rv.Uint(), 10)
	case reflect.Float64:
		return strconv.FormatFloat(rv.Float(), 'g', -1, 64)
	case reflect.Float32:
		return strconv.FormatFloat(rv.Float(), 'g', -1, 32)
	case reflect.Bool:
		return strconv.FormatBool(rv.Bool())
	}
	return fmt.Sprintf("%v", src)
}

func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) {
	switch rv.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.AppendInt(buf, rv.Int(), 10), true
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.AppendUint(buf, rv.Uint(), 10), true
	case reflect.Float32:
		return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true
	case reflect.Float64:
		return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true
	case reflect.Bool:
		return strconv.AppendBool(buf, rv.Bool()), true
	case reflect.String:
		s := rv.String()
		return append(buf, s...), true
	}
	return
}
func convertAssignRows(dest interface{}, src string) error {
	// Common cases, without reflect.
	switch d := dest.(type) {
	case *string:
		if d == nil {
			return errNilPtr
		}
		*d = src
		return nil
	case *[]byte:
		if d == nil {
			return errNilPtr
		}
		*d = []byte(src)
		return nil
	case *bool:
		l := strings.ToLower(src)
		if l == "yes" || l == "true" || l == "是" || l == "1" {
			*d = true
		}
		return nil
	}
	var sv reflect.Value

	if scanner, ok := dest.(Scanner); ok {
		return scanner.Excel(src)
	}
	if scanner, ok := dest.(sql.Scanner); ok {
		return scanner.Scan(src)
	}

	dpv := reflect.ValueOf(dest)
	if dpv.Kind() != reflect.Ptr {
		return errors.New("destination not a pointer")
	}
	if dpv.IsNil() {
		return errNilPtr
	}

	if !sv.IsValid() {
		sv = reflect.ValueOf(src)
	}

	dv := reflect.Indirect(dpv)
	if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) {
		dv.Set(sv)
		return nil
	}

	if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) {
		dv.Set(sv.Convert(dv.Type()))
		return nil
	}
	switch dv.Kind() {
	case reflect.Ptr:
		if src == "" {
			dv.Set(reflect.Zero(dv.Type()))
			return nil
		}
		dv.Set(reflect.New(dv.Type().Elem()))
		return convertAssignRows(dv.Interface(), src)
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		s := asString(src)
		i64, err := strconv.ParseInt(s, 10, dv.Type().Bits())
		if err != nil {
			err = strconvErr(err)
			return fmt.Errorf("converting type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
		}
		dv.SetInt(i64)
		return nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		s := asString(src)
		u64, err := strconv.ParseUint(s, 10, dv.Type().Bits())
		if err != nil {
			err = strconvErr(err)
			return fmt.Errorf("converting type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
		}
		dv.SetUint(u64)
		return nil
	case reflect.Float32, reflect.Float64:
		s := asString(src)
		f64, err := strconv.ParseFloat(s, dv.Type().Bits())
		if err != nil {
			err = strconvErr(err)
			return fmt.Errorf("converting type %T (%q) to a %s: %v", src, s, dv.Kind(), err)
		}
		dv.SetFloat(f64)
		return nil
	case reflect.String:
		dv.SetString(src)
		return nil
	}
	return fmt.Errorf("unsupported Scan, storing type %T into type %T", src, dest)

}
func Scan(target interface{}, columns ...string) error {
	tar := reflect.ValueOf(target)
	if tar.Kind() != reflect.Ptr {
		return fmt.Errorf("对象必需是指针")
	}

	tar = tar.Elem()
	if tar.Kind() != reflect.Struct {
		return fmt.Errorf("对象必需是结构体")
	}
	for i := 0; i < tar.NumField(); i++ {
		if i < len(columns) {
			v := tar.Field(i)
			if v.Kind() == reflect.Ptr {
				if e := convertAssignRows(v.Addr().Interface(), columns[i]); e != nil {
					return e
				}
			} else {
				reflectValue := reflect.New(reflect.PtrTo(v.Type()))
				reflectValue.Elem().Set(v.Addr())
				if e := convertAssignRows(reflectValue.Interface(), columns[i]); e != nil {
					return e
				}
				v.Set(reflectValue.Elem().Elem())
			}
		}
	}
	return nil
}
